//
// Created by QU on 2024/4/23.
//

/*
当定义一个类时，我们显式地或隐式地指定在此类型的对象拷贝、移动、赋值和销毁时做什么。
一个类通过定义五种特殊的成员函数来控制这些操作，包括：
    拷贝构造函数（copy constructor）、
    拷贝赋值运算符（copy-assignment operator）、
    移动构造函数（move constructor）、
    移动赋值运算符（move-assignment operator）和
    析构函数（destructor）。

拷贝和移动构造函数定义了当用同类型的另一个对象初始化本对象时做什么。
拷贝和移动赋值运算符定义了将一个对象赋予同类型的另一个对象时做什么。
析构函数定义了当此类型对象销毁时做什么。我们称这些操作为拷贝控制操作（copy control）。
 如果一个类没有定义所有这些拷贝控制成员，编译器会自动为它定义缺失的操作。因此，很多类会忽略这些拷贝控制操作（参见7.1.5节，第239页）。
 但是，对一些类来说，依赖这些操作的默认定义会导致灾难。通常，实现拷贝控制操作最困难的地方是首先认识到什么时候需要定义这些操作。

*/


/*
    拷贝构造函数（copy constructor）、
    如果一个构造函数的第一个参数是自身类类型的引用，且任何额外参数都有默认值，则此构造函数是拷贝构造函数。
    class Foo{
    public:
        Foo();          // default constructor
        Foo(const Foo &)    // copy constructor
        // ...
    }
    拷贝构造函数在几种情况下都会被隐式地使用。因此，拷贝构造函数通常不应该是explicit的;
    如果我们没有为一个类定义拷贝构造函数，编译器会为我们定义一个。

    拷贝初始化不仅在我们用=定义变量时会发生，在下列情况下也会发生
        ·将一个对象作为实参传递给一个非引用类型的形参
        ·从一个返回类型为非引用类型的函数返回一个对象
        ·用花括号列表初始化一个数组中的元素或一个聚合类中的
    当我们初始化标准库容器或是调用其insert或push成员时，容器会对其元素进行拷贝初始化。
    与之相对，用emplace成员创建的元素都进行直接初始化.

    在函数调用过程中，具有非引用类型的参数要进行拷贝初始化（参见6.2.1节，第188页）。
    类似的，当一个函数具有非引用的返回类型时，返回值会被用来初始化调用方的结果（参见6.3.2节，第201页）。

    拷贝构造函数被用来初始化非引用类类型参数，这一特性解释了为什么拷贝构造函数自己的参数必须是引用类型。
    如果其参数不是引用类型，则调用永远也不会成功一为了调用拷贝构造函数，我们必须拷贝它的实参，但为了拷贝实参，我们又需要调用拷贝构造函数，如此无限循环。

    可以通过explicit关键字限制必须显式调用.
    使用 xx函数()=default显示指定编译器生成 默认 构造/拷贝/赋值(operator=)/析构 函数
    使用 xx函数()=delete显示指定编译器删除 默认 构造/拷贝/赋值(operator=) 函数  (没有析构函数)

    如果一个类需要自定义析构函数，几乎可以肯定它也需要自定义拷贝赋值运算符和拷贝构造函数。
    如果一个类需要一个拷贝构造函数，几乎可以肯定它也需要一个拷贝赋值运算符.
    反之亦然,一如果一个类需要一个拷贝赋值运算符，几乎可以肯定它也需要一个拷贝构造函数。
    然而，无论是需要拷贝构造函数还是需要拷贝赋值运算符都不必然意味着也需要析构函数。
*/

/*
 * unique_ptr不能拷贝，但有一个例外一一-将要被销毁的unique_ptr是可以拷贝或销毁的。
 * 因此，在 418页的clone函数中返回局部unique_ptr对象ret是可以的，因为ret马上就要被销毁了。
 * 而此时的“拷贝”其实是触发移动构造函数进行了移动。

unique_ptr<int> clone(int p){
    unique_ptr<int> ret(new int (p));
    // ...
    return ret;
}

unique_ptr<int> clone(int p){
    return unique_ptr<int>(new int (p));
}
// 上面两个使用了移动赋值函数, 这样的例外不用担心被释放.

 */

/* 如果sorted定义如下, 会发生什么
 * Foo Foo::sorted() const &{
 *      Foo ret(*this);
 *      return ret.sorted();
 * }
 *
 * 首先，局部变量ret拷贝了被调用对象的一个副本。
 * 然后，对ret调用sorted，由于并非是函数返回语句或函数结束（虽然写成一条语句，但执行过程是先调用sorted，然后将结果返回），
 * 因此编译器认为它是左值，仍然调用左值引用版本，产生递归循环。利用右值引用版本来完成排序的期望不能实现。
 *
 * 如果定义成这样呢?
 * Foo Foo::sorted()const &
 * {
 *      return Foo(*this).sorted();
 * }
 * 这样的写法可以正确利用右值引用版本来完成排序。
 * 原因在于，编译器认为Foo（*this）是一个“无主”的右值，对它调用sorted会匹配右值引用版本。
 *
 */

#include <iostream>
#include <memory>
#include <utility>
#include <string>
#include <algorithm>
#include <vector>
#include <functional>

using namespace std;

int main([[maybe_unused]] int argc, [[maybe_unused]] const char *argv[]) {
    vector<int> vec = {1234, 1, 2343, 3421, 342, 12, 1, 23, 4, 4, 2354345, 254, 234, 234, 523, 45, 234, 51, 234, 234, 5,
                       4567, 76, 3442365, 345, 2345, 245, 3, 24535};

    int count = count_if(vec.begin(), vec.end(), std::bind(greater<int>(), std::placeholders::_1,1024));
    cout << count << " times." << std::unitbuf << std::endl;
//    count_if(vec.begin(), vec.end(), [](auto && PH1) { return greater<int>()(1024, std::forward<decltype(PH1)>(PH1)); });

    /*
    在C++中，std::bind 函数是用来绑定函数调用的参数的，它属于<functional>头文件。
     当你使用 std::bind 来绑定 std::greater<int>()、一个具体的整数 1024 和一个占位符 std::placeholders::_1 时，你创建了一个新的函数对象。

    std::greater<int>() 是一个函数对象，它接受两个整数参数，当第一个参数大于第二个参数时返回 true，否则返回 false。
     在你的例子中，通过 std::bind 绑定之后的情况是这样的：

    第一个参数（1024）已被绑定为 std::greater<int>() 的第一个参数。
    第二个参数（std::placeholders::_1）则是一个占位符，代表这个新函数对象将接受一个参数，并将这个参数作为 std::greater<int>() 的第二个参数。
    因此，std::bind(greater<int>(), 1024, std::placeholders::_1) 返回的是一个新的函数对象，这个对象可以被看作是一个单参数的函数，这个参数记为 x。当这个函数被调用时，它实际上执行的是 std::greater<int>()(1024, x)。这意味着函数将检查 1024 是否大于它的参数 x。

    换句话说，这个绑定创建了一个函数，这个函数检查一个给定的数是否小于 1024。如果是，函数返回 true；如果不是，返回 false。
     */


    return 0;
}